package org.geektimes.projects.userweb.hash;

import org.geektimes.projects.userweb.hash.IHashService;

import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @Author: jipeng
 * @Description:
 * @Date: Created in 2021/5/18 6:59
 */
public class ConsistentHash<T> {

    private final IHashService hashService;
    private final int numberOfReplicas;
    private final SortedMap<Long,T> circle = new TreeMap<>();

    public ConsistentHash(IHashService hashService, int numberOfReplicas, Collection<T> nodes){
        this.hashService = hashService;
        this.numberOfReplicas = numberOfReplicas;
        for(T node : nodes) {
            addNode(node);
        }
    }

    /**
     * 相应节点
     * @param node
     */
    public void addNode(T node){
        for(int i =0;i< this.numberOfReplicas; i++){
            this.circle.put(this.hashService.hash(node.toString()+i),node);
        }
    }

    /**
     * 移除节点
     * @param node
     */
    public void removeNode(T node){
        for(int i =0;i< this.numberOfReplicas; i++){
            this.circle.remove(this.hashService.hash(node.toString()+i));
        }
    }

    /**
     * 获取节点
     * @param key
     * @return
     */
    public T get(String key){
        if(circle.isEmpty()) {
            return null;
        }
        //获取hash值
        long hash = hashService.hash(key);

        if(!circle.containsKey(hash)){
            //找出 circle 中 键 比 hash 大的数据，重新赋值给  tailMap
            //tailMap函数 => 返回此映射的部分视图，其键的范围大于等于hash。
            SortedMap<Long,T> tailMap = circle.tailMap(hash);
            // 如果 tailMap 为空，则返回 环形中第一个键，否则返回 tailMap中的第一个键
            // firstKey => 返回此映射中当前的第一个键。
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        //通过键获取值，拿到响应的节点
        return circle.get(hash);
    }
}
